package com.xiguanlezz.cn.codec;

import com.xiguanlezz.cn.CodecUtils;
import com.xiguanlezz.cn.ProtocolConst;
import com.xiguanlezz.cn.protocol.*;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;

import java.nio.charset.StandardCharsets;
import java.util.List;

/**
 * @Author：习惯了种种
 * @CreateDate：2024/11/4 13:04
 * @Description：Rpc消息解码器
 */
public class RpcDecoder extends ByteToMessageDecoder
        // 为什么要实现这个鬼接口，接口只是提供了某个序列化器。因为Java只支持单继承
        implements RpcCodec {

    @Override
    protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf in, List<Object> outList) throws Exception {
        // 1. 处理请求头拆包问题
        // 引入Const.HEADER_TOTAL_LENGTH判断可读字节数是为了解决发送端请求头拆包问题
        if (!in.isReadable() || in.readableBytes() < CodecUtils.HEADER_TOTAL_LENGTH) {
            return;
        }

        // 标记一下读取请求体前的idx
        in.markReaderIndex();

        // 2. 按照数据协议读取数据
        short magicNumber = in.readShort();
        if (ProtocolConst.MAGIC != magicNumber) {
            throw new IllegalArgumentException("magicNumber: " + magicNumber + " is illegal");
        }
        byte messageType = in.readByte();
        byte messageStatus = in.readByte();
        long messageId = in.readLong();
        CharSequence serializerType = in.readCharSequence(ProtocolConst.SERIALIZE_TYPE_LENGTH, StandardCharsets.UTF_8);
        int dataLength = in.readInt();

        if (in.readableBytes() < dataLength) {
            // 说明此时请求体部分发生了拆包。此时需要将之间已经读取过的header字节也复原
            // byteBuf.resetReaderIndex()需要和byteBuf.markReaderIndex()方法搭配使用
            in.resetReaderIndex();
        }

        RpcHeader rpcHeader = RpcHeader.builder()
                .magicNumber(magicNumber)
                .messageType(messageType)
                .messageStatus(messageStatus)
                .messageId(messageId)
                .serializerType((String) serializerType)
                .dataLength(dataLength)
                .build();

        // 3. 根据消息类型确定消息体类型
        Class<? extends RpcMessage> clazz = getClassByMessageType(RpcMessageType.findMessageType(messageType));

        // 4. 读取消息体中的数据
        byte[] bs = new byte[dataLength];
        in.readBytes(bs);

        // 5. 将读取的消息体数据反序列化
        RpcMessage body = getJdkSerializer().deserialize(bs, clazz);

        // 6. 组装实体
        RpcProtocol<? extends RpcMessage> rpcProtocol = new RpcProtocol<>(rpcHeader, body);

        // 7. 将请求体数据解码后，并添加到解码列表中
        outList.add(rpcProtocol);
    }

    private static Class<? extends RpcMessage> getClassByMessageType(RpcMessageType messageType) {
        switch (messageType) {
            case REQUEST:
                return RpcRequest.class;
            case RESPONSE:
                return RpcResponse.class;
            case HEARTBEAT:
                // TODO(xiguanlezz)：心跳包逻辑补充
                break;
        }
        return null;
    }
}
